package com.joker.demo.core.netty.server;


import com.joker.demo.core.config.JobSchedulerProperties;
import com.joker.demo.core.domain.dto.MessageDTO;
import com.joker.demo.core.exception.BizException;
import com.joker.demo.core.netty.server.handler.NettyServerHandler;
import com.joker.demo.core.netty.server.handler.ServerProtocolRouterHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.json.JsonObjectDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Netty调度器服务器
 *
 * @author Joker
 * @since 2025-07-26
 */
public class NettyServer {


    private static final Logger log = LoggerFactory.getLogger(NettyServer.class);

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ChannelFuture channelFuture;


    private final NettyServerHandler nettyServerHandler;
    private final JobSchedulerProperties config;

    public NettyServer(NettyServerHandler nettyServerHandler, JobSchedulerProperties config) {
        this.nettyServerHandler = nettyServerHandler;
        this.config = config;
    }

    public void connect() {


        int port = config.getPort();
        int bossThreads = config.getBossThreads();
        int workerThreads = config.getWorkerThreads();
        log.info("启动Netty服务器，端口：{}", port);

        bossGroup = new NioEventLoopGroup(bossThreads);
        workerGroup = new NioEventLoopGroup(workerThreads);

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline p = ch.pipeline();
                            // 协议探测器
                            p.addLast(new ServerProtocolRouterHandler());
                            // 入栈
                            p.addLast(new JsonObjectDecoder());
                            // 入栈 - 指定UTF-8编码
                            p.addLast(new StringDecoder(CharsetUtil.UTF_8));
                            // 出栈 - 指定UTF-8编码
                            p.addLast(new StringEncoder(CharsetUtil.UTF_8));
                            p.addLast(nettyServerHandler);
                            // log 是双端处理
                            p.addLast(new LoggingHandler(LogLevel.DEBUG));
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            channelFuture = bootstrap.bind(port).sync();
            log.info("Netty服务器启动成功，监听端口：{}", port);

        } catch (InterruptedException e) {
            log.error("启动Netty服务器失败", e);
            Thread.currentThread().interrupt();
        } catch (Exception e) {
            throw new BizException("启动Netty服务器失败", e);
        }
    }


    public void stop() {
        log.info("关闭Netty服务器");

        if (channelFuture != null) {
            channelFuture.channel().close();
        }

        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }

        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }

        log.info("Netty服务器已关闭");
    }


    public boolean sendMessageToExecutor(String registerKey, MessageDTO message) {
       return nettyServerHandler.sendMessageToExecutor(registerKey, message);
    }

} 